package br.mikhas.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Some util methods to help working with java primitive types and it's wrappers
 * 
 * @author Mikhail Domanoski
 * 
 */

public class TypeUtils {

	/**
	 * Holds a mapping between primitives and wrapper classes
	 */
	private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER;
	/**
	 * Holds a mapping between wrapper and primitive classes
	 */
	private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE;
	/**
	 * Holds wrapper classes
	 */
	private static final Set<Class<?>> WRAPPERS;
	/**
	 * Holds primitive classes
	 */
	private static final Set<Class<?>> PRIMITIVES;

	// Initialize sets and maps
	static {
		WRAPPERS = new HashSet<Class<?>>(9);
		WRAPPERS.add(Character.class);
		WRAPPERS.add(Boolean.class);
		WRAPPERS.add(Long.class);
		WRAPPERS.add(Integer.class);
		WRAPPERS.add(Short.class);
		WRAPPERS.add(Float.class);
		WRAPPERS.add(Byte.class);
		WRAPPERS.add(Double.class);
		WRAPPERS.add(Void.class);

		PRIMITIVES = new HashSet<Class<?>>(9);
		PRIMITIVES.add(char.class);
		PRIMITIVES.add(boolean.class);
		PRIMITIVES.add(long.class);
		PRIMITIVES.add(int.class);
		PRIMITIVES.add(short.class);
		PRIMITIVES.add(float.class);
		PRIMITIVES.add(byte.class);
		PRIMITIVES.add(double.class);
		PRIMITIVES.add(void.class);

		PRIMITIVE_TO_WRAPPER = new HashMap<Class<?>, Class<?>>(9);
		PRIMITIVE_TO_WRAPPER.put(char.class, Character.class);
		PRIMITIVE_TO_WRAPPER.put(boolean.class, Boolean.class);
		PRIMITIVE_TO_WRAPPER.put(long.class, Long.class);
		PRIMITIVE_TO_WRAPPER.put(int.class, Integer.class);
		PRIMITIVE_TO_WRAPPER.put(short.class, Short.class);
		PRIMITIVE_TO_WRAPPER.put(float.class, Float.class);
		PRIMITIVE_TO_WRAPPER.put(byte.class, Byte.class);
		PRIMITIVE_TO_WRAPPER.put(double.class, Double.class);
		PRIMITIVE_TO_WRAPPER.put(void.class, Void.class);

		WRAPPER_TO_PRIMITIVE = new HashMap<Class<?>, Class<?>>(9);
		WRAPPER_TO_PRIMITIVE.put(Character.class, char.class);
		WRAPPER_TO_PRIMITIVE.put(Boolean.class, boolean.class);
		WRAPPER_TO_PRIMITIVE.put(Long.class, long.class);
		WRAPPER_TO_PRIMITIVE.put(Integer.class, int.class);
		WRAPPER_TO_PRIMITIVE.put(Short.class, short.class);
		WRAPPER_TO_PRIMITIVE.put(Double.class, double.class);
		WRAPPER_TO_PRIMITIVE.put(Float.class, float.class);
		WRAPPER_TO_PRIMITIVE.put(Byte.class, byte.class);
		WRAPPER_TO_PRIMITIVE.put(Void.class, void.class);

	}

	private TypeUtils() {
	}

	/**
	 * Converts a String to a desired java type
	 * 
	 * @param type
	 *            The type to get
	 * @param value
	 *            The string value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T convertToType(Class<T> type, String value) {
		if (type == String.class) {
			return (T) String.valueOf(value);
		} else if (Numbers.isNumber(type))
			return (T) Numbers.getNumber((Class<? extends Number>) type, value);
		else if (type == Boolean.class || type == boolean.class) {
			return (T) Boolean.valueOf(value);
		} else if (type == char.class || Character.class == type) {
			return (T) Character.valueOf(value.charAt(0));
		} else {
			return null;
		}
	}

	/**
	 * Gets the primitive java type according to the given wrapper.
	 * 
	 * @param cls
	 *            The java wrapper class.
	 *            <p>
	 *            If it is not a wrapper class, the method will return the class
	 *            it self.
	 * @return The primitive class
	 */
	public static Class<?> getPrimitive(Class<?> cls) {
		if (WRAPPERS.contains(cls)) {
			return WRAPPER_TO_PRIMITIVE.get(cls);
		} else {
			return cls;
		}

	}

	/**
	 * Gets the wrapper java type according to the given primitive.
	 * 
	 * @param cls
	 *            The java primitive class.
	 *            <p>
	 *            If it is not a primitive class, the method will return the
	 *            class it self.
	 * @return The wrapper class
	 */
	public static Class<?> getWrapper(Class<?> cls) {
		if (PRIMITIVES.contains(cls)) {
			return PRIMITIVE_TO_WRAPPER.get(cls);
		} else {
			return cls;
		}

	}

	/**
	 * Checks if the given class is a java primitive wrapper
	 * 
	 * @param clazz
	 *            the class to check
	 * @return <code>true</code> if it is a java wrapper class
	 */
	public static boolean isWrapper(Class<?> clazz) {
		return WRAPPERS.contains(clazz);
	}

	/**
	 * Checks if the given class is a java primitive class
	 * 
	 * @param clazz
	 *            the class to check
	 * @return <code>true</code> if it is a java primitive class
	 */
	public static boolean isPrimitive(Class<?> clazz) {
		return PRIMITIVES.contains(clazz);
	}
}
